package plugins.CENO.Common;
import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import javax.crypto.Cipher;
import freenet.support.Base64;
import freenet.support.IllegalBase64Exception;
import freenet.support.Logger;
public final class Crypto {
private static final int KEY_SIZE = 4096;
private static final String KEY_ALGORITHM = "RSA";
private static final String CIPHER_TRANSFORMATION = "RSA/None/OAEPWithSHA256AndMGF1Padding";
private static final String SECURITY_PROVIDER = "BC";
private static final SecureRandom srng = new SecureRandom();
private Crypto() {}
public static KeyPair generateAsymKey() throws NoSuchAlgorithmException, NoSuchProviderException {
KeyPairGenerator generator = null;
generator = KeyPairGenerator.getInstance(KEY_ALGORITHM, SECURITY_PROVIDER);
generator.initialize(KEY_SIZE, srng);
Long startTime = System.currentTimeMillis();
KeyPair pair = generator.generateKeyPair();
Logger.normal(Crypto.class, "Generated new RSA key in " +
TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis() - startTime) + " seconds");
return pair;
}
public static PrivateKey loadPrivateKey(String key64) throws GeneralSecurityException, IllegalBase64Exception {
byte[] clear = Base64.decodeStandard(key64);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(clear);
KeyFactory fact = KeyFactory.getInstance(KEY_ALGORITHM, SECURITY_PROVIDER);
PrivateKey priv = fact.generatePrivate(keySpec);
Arrays.fill(clear, (byte) 0);
return priv;
}
public static PublicKey loadPublicKey(String key64) throws GeneralSecurityException, IllegalBase64Exception {
byte[] data = Base64.decodeStandard(key64);
X509EncodedKeySpec spec = new X509EncodedKeySpec(data);
KeyFactory fact = KeyFactory.getInstance(KEY_ALGORITHM, SECURITY_PROVIDER);
return fact.generatePublic(spec);
}
public static String savePrivateKey(PrivateKey priv) throws GeneralSecurityException {
KeyFactory fact = KeyFactory.getInstance(KEY_ALGORITHM, SECURITY_PROVIDER);
PKCS8EncodedKeySpec spec = fact.getKeySpec(priv, PKCS8EncodedKeySpec.class);
byte[] packed = spec.getEncoded();
String key64 = Base64.encodeStandard(packed);
Arrays.fill(packed, (byte) 0);
return key64;
}
public static String savePublicKey(PublicKey publ) throws GeneralSecurityException {
KeyFactory fact = KeyFactory.getInstance(KEY_ALGORITHM, SECURITY_PROVIDER);
X509EncodedKeySpec spec = fact.getKeySpec(publ, X509EncodedKeySpec.class);
return Base64.encodeStandard(spec.getEncoded());
}
public static byte[] encrypt(byte[] msg, String pubKey64) throws GeneralSecurityException, IllegalBase64Exception {
if (msg.length > KEY_SIZE/8) {
throw new GeneralSecurityException("Cannot encrypt payload of that size");
}
PublicKey pubKey = loadPublicKey(pubKey64);
Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORMATION, SECURITY_PROVIDER);
cipher.init(Cipher.ENCRYPT_MODE, pubKey, srng);
byte[] cipherText = cipher.doFinal(msg);
return cipherText;
}
public static byte[] decrypt(byte[] msg, PrivateKey privKey) throws GeneralSecurityException {
Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORMATION, SECURITY_PROVIDER);
cipher.init(Cipher.DECRYPT_MODE, privKey);
//TODO Handle BadPaddingException (somebody has tampered with the data) without giving an oracle
byte[] plainText = cipher.doFinal(msg);
return plainText;
}
public static byte[] decrypt(byte[] msg, String privKey64) throws GeneralSecurityException, IllegalBase64Exception {
PrivateKey privKey = loadPrivateKey(privKey64);
return decrypt(msg, privKey);
}
public static boolean isValidKeypair(KeyPair keyPair) throws GeneralSecurityException, IllegalBase64Exception, UnsupportedEncodingException {
if (keyPair == null) {
return false;
}
return isValidKeypair(savePublicKey(keyPair.getPublic()), savePrivateKey(keyPair.getPrivate()));
}
public static boolean isValidKeypair(String pubKey64, String privKey64) throws UnsupportedEncodingException, GeneralSecurityException, IllegalBase64Exception {
if (pubKey64 == null || privKey64 == null) {
return false;
}
byte[] cipherText = encrypt("Hello".getBytes("UTF-8"), pubKey64);
String msg = new String(decrypt(cipherText, privKey64), "UTF-8");
if (msg.equals("Hello")) {
return true;
}
return false;
}
}